﻿using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SuperSocket.Connection;
using SuperSocket.Server;
using SuperSocket.Server.Abstractions;
using SuperSocket.Server.Abstractions.Session;
using SuperSocket.Server.Host;
using SuperSocket.WebSocket;
using SuperSocket.WebSocket.Server;
using System.Collections.Concurrent;

namespace Micro.WebSocket
{
    public abstract partial class WebSocketHost
    {
        /// <summary>
        /// 是否清除长时间没通信的客户端
        /// </summary>
        public virtual bool ClearIdleSession { get; set; }

        /// <summary>
        /// 服务端定时器
        /// </summary>
        public virtual Timer ServerTimer { get; set; }

        /// <summary>
        /// 默认端口
        /// </summary>
        private const string DefaultPort = "8018";

        /// <summary>
        /// 监听端口：（多个端口之间用逗号隔开）
        /// </summary>
        public abstract string Prots { get; set; }

        /// <summary>
        /// 客户端Sessions字典
        /// </summary>
        public ConcurrentDictionary<string, WebSocketClientDto> sessionDic { get; set; } = new ConcurrentDictionary<string, WebSocketClientDto>();

        /// <summary>
        /// WebSocket服务
        /// </summary>
        public IHost ServerHost;

        /// <summary>
        /// 服务端推送消息
        /// </summary>
        /// <param name="session"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public virtual async ValueTask Publish(string sessionId, dynamic data, string clientUniqueId = default)
        {
            var webSocketClientDto = sessionDic.Where(o => o.Value is not null).OrderByDescending(o => o.Value.Client.LastActiveTime)
                .FirstOrDefault(o => o.Value.Client.State == SessionState.Connected && ((!string.IsNullOrEmpty(o.Value.ClientUniqueId) && o.Value.ClientUniqueId == clientUniqueId) || o.Value.Client.SessionID == sessionId)).Value;
            if (webSocketClientDto is null)
            {
                Console.WriteLine($"【{GetType().Name}】：推送消息异常：客户端不存在或客户端连接已断开！"); return;
            }
            try
            {
                var sendStr = data.GetType() == typeof(string) ? data : JsonConvert.SerializeObject(data);
                await ((WebSocketSession)webSocketClientDto.Client).SendAsync(sendStr);
                Console.WriteLine($"【{GetType().Name}】：服务端发送： 消息=【{sendStr}】");
            }
            catch { }
        }

        #region WebSocket服务端

        /// <summary>
        /// 启动WebSocket服务端
        /// </summary>
        /// <returns></returns>
        public virtual async ValueTask StartAsync(CancellationToken cancellationToken)
        {
            if (string.IsNullOrEmpty(Prots)) Prots = DefaultPort;
            var protList = Prots.Split(',').Select(int.Parse).Select(o => new ListenOptions { Ip = "Any", Port = o, BackLog = 1024 }).ToList();

            var builder = WebSocketHostBuilder.Create()
            //.ConfigureServices((hostContext, services) =>
            //{
            //})
            //配置参数
            .ConfigureSuperSocket(options =>
            {
                options.Name = Guid.NewGuid().ToString();
                //options.MaxPackageLength = 1024 * 1024;
                //options.ReceiveBufferSize = 4 * 1024;
                //options.SendBufferSize = 4 * 1024;
                options.ReceiveTimeout = 30;
                options.SendTimeout = 30;
                if (ClearIdleSession)
                {
                    options.ClearIdleSessionInterval = 30 * 1; // 清空空闲会话的时间间隔, 默认值是120, 单位为秒;
                    options.IdleSessionTimeOut = 65 * 1;//会话空闲超时时间; 当此会话空闲时间超过此值，同时clearIdleSession被配置成true时，此会话将会被关闭; 默认值为300，单位为秒;
                }
                options.Listeners = protList;
            });

            if (ClearIdleSession)
                //使用清理闲置会话
                builder.UseClearIdleSession();
            //使用会话的连接和断开事件
            builder.UseSessionHandler(OnConnectedAsync, OnClosedAsync)
            //使用会话的数据接收时间
            .UseWebSocketMessageHandler(OnReceiveMessageAsync)
            //使用会话容器
            .UseInProcSessionContainer();
            //日志
            ServerHost = builder.ConfigureLogging((hostCtx, loggingBuilder) =>
            {
                //register your logging library here
                loggingBuilder.AddConsole();
            }).Build();

            //启动
            ServerHost.StartAsync().GetAwaiter().GetResult();

            await Task.Run(() =>
            {
                // 创建并启动 Timer 实例，并设置定时器间隔为5分钟
                ServerTimer = new Timer(_ =>
                {
                    try
                    {
                        Console.WriteLine($"【{GetType().Name}】【{Prots}】：客户端数量：{sessionDic.Count()}");
                    }
                    catch { }
                }, default, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60 * 5));
            });

            Console.WriteLine($"【WebSocket：{GetType().Name}】服务启动成功，端口【{Prots}】！");
        }

        /// <summary>
        /// 停止WebSocket服务端
        /// </summary>
        /// <returns></returns>
        public virtual async ValueTask StopServer()
        {
            try
            {
                foreach (var v in sessionDic)
                {
                    await v.Value.Client.CloseAsync(SuperSocket.Connection.CloseReason.ServerShutdown);
                }
                sessionDic.Clear();
                ServerHost = default;
                Console.WriteLine($"【{GetType().Name}】：WebSocket服务端结束成功【StopAsync】 ");
            }
            catch { }
        }

        /// <summary>
        /// 客户端连接
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        public virtual async ValueTask OnConnectedAsync(IAppSession session)
        {
            await ValueTask.CompletedTask;
            var sessionImp = session as WebSocketSession;

            #region 用户名密码较验

            if (sessionImp is null || string.IsNullOrEmpty(sessionImp?.Path))
            {
                //await session.CloseAsync(SuperSocket.Connection.CloseReason.ServerShutdown);
                //Console.WriteLine($"【{GetType().Name}】：用户名密码不存在！");
                //return;
            }
            else
            {
                //var dicParams = QueryHelpers.ParseQuery(sessionImp.Path);
                //if (!(dicParams.ContainsValue(string.Empty) && dicParams.ContainsValue(string.Empty)))
                //{
                //    Console.WriteLine($"【{GetType().Name}】：用户名密码有误！");
                //    await session.CloseAsync(SuperSocket.Connection.CloseReason.ServerShutdown); return;
                //}
            }

            #endregion 用户名密码较验

            while (!sessionDic.ContainsKey(session.SessionID))
            {
                //添加不成功则重复添加
                if (!sessionDic.TryAdd(session.SessionID, new WebSocketClientDto() { Client = session }))
                    Thread.Sleep(1);
                Console.WriteLine($"【{GetType().Name}】：WebSocket客户端连接_OnConnectedAsync：客户端ID: 【{session.SessionID}】连接成功！");
            }
        }

        /// <summary>
        /// 客户端断开连接
        /// </summary>
        /// <param name="session"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        public virtual async ValueTask OnClosedAsync(IAppSession session, CloseEventArgs e)
        {
            await ValueTask.CompletedTask;
            while (sessionDic.ContainsKey(session.SessionID))
            {
                //移除不成功则重复移除
                if (!sessionDic.TryRemove(session.SessionID, out WebSocketClientDto? webSocketClientDto))
                    Thread.Sleep(1);
                Console.WriteLine($"【{GetType().Name}】：WebSocket客户端连接_OnClosedAsync：客户端ID: 【{session.SessionID}】断开连接！");
            }
        }

        /// <summary>
        /// 消息接收
        /// </summary>
        /// <param name="session"></param>
        /// <param name="getData"></param>
        /// <returns></returns>
        public virtual async ValueTask OnReceiveMessageAsync(IAppSession session, WebSocketPackage webSocketPackage)
        {
            var getMsg = webSocketPackage.Message;
            try
            {
                Console.WriteLine($"【{GetType().Name}】：消息接收：客户端ID=【{session.SessionID}】 消息=【{getMsg}】 当前连接数【{sessionDic.Count}】！");
                await Publish(session.SessionID, getMsg);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"【{GetType().Name}】：接收数据异常，数据源：{getMsg}", ex);
            }
            await ValueTask.CompletedTask;
        }

        #endregion WebSocket服务端
    }

    public partial class WebSocketClientDto
    {
        public string ClientUniqueId { get; set; }
        public IAppSession Client { get; set; }
    }
}